﻿/*
Copyright (c) 2010, hiSoft
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the hiSoft nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE HISOFT AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL HISOFT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;

namespace Orchard.Test.Library
{
    public class CommentHelper : UIHelperBase
    {
        public CommentHelper(TestLibrary settings) : base(settings) { }

        private int GetIndexOfComment(Comment comment)
        {
            ManageComments();
            var count = (int)selenium.GetXpathCount(CommentsLocator);            
            var index = 1;
            for (; index <= count; index++)
            {
                var locatorCommentedOn = String.Format("{0}[{1}]/td[5]/a", CommentsLocator, index);
                var commentedOnTitle = selenium.GetText(locatorCommentedOn);

                if (comment.CommentedOn is Post)
                {
                    Post post = comment.CommentedOn as Post;
                    if (String.CompareOrdinal(commentedOnTitle, post.Title) != 0)
                        continue;
                }
                else if (comment.CommentedOn is Page)
                {
                    Page page = comment.CommentedOn as Page;
                    if (String.CompareOrdinal(commentedOnTitle, page.Title) != 0)
                        continue;
                }

                var locatorAuthor = String.Format("{0}[{1}]/td[3]", CommentsLocator, index);
                var author = selenium.GetText(locatorAuthor);

                if (String.CompareOrdinal(author, comment.Author.Name) != 0)
                    continue;

                var locatorComment = String.Format("{0}[{1}]/td[4]/p[2]", CommentsLocator, index);
                var commentDescription = selenium.GetText(locatorComment);
                if (String.CompareOrdinal(commentDescription, comment.CommentText) != 0)
                    continue;

                //var locatorDate = String.Format("{0}[{1}]/td[4]/p[1]/a/time", CommentsLocator, index);
                var locatorDate = String.Format("{0}[{1}]/td[4]/p[1]/a", CommentsLocator, index);
                var date = selenium.GetText(locatorDate);
                //bug#202:Comment time is not local time but UTC time, so add 8 hours
                var commentDate = DateTime.Parse(date);// +TimeSpan.FromHours(8), bug#202fixed

                if ((commentDate.Day == comment.DateTime.Day) && (commentDate.Hour == comment.DateTime.Hour) && (commentDate.Minute == comment.DateTime.Minute))
                    break;
            }

            if (index > count)
            {
                return -1;
            }

            return index;
        }

        private string GetStatusOfCommentIndex(int index)
        {
            var locatorStatus = String.Format("{0}[{1}]/td[2]", CommentsLocator, index);
            return selenium.GetText(locatorStatus);
        }

        private void CheckComment(Comment comment)
        {
            int index = GetIndexOfComment(comment);
            selenium.Check(String.Format("{0}[{1}]/td[1]/input[2]", CommentsLocator, index));
        }

        private int GetActionIndexByText(int commentIndex, string actionText)
        {
            string actionsLocator = String.Format("{0}[{1}]/td[6]/div/a", CommentsLocator, commentIndex);
            var actionCount = (int)selenium.GetXpathCount(actionsLocator);
            for (int i = 1; i <= actionCount; i++)
            {
                if (selenium.GetText(String.Format("{0}[{1}]", actionsLocator, i)) == actionText)
                {
                    return i;
                }
            }
            return 0;
        }

        public void ManageComments()
        {
            if (!selenium.GetLocation().Contains("/Admin/Comments"))
            {
                selenium.Open("/Admin/Comments");  
            }             
        }

        public Comment CommentOn(IContent commentedOn)
        {
            return CommentOn(commentedOn,true);
        }

        public Comment CommentOn(IContent commentOn, bool isAuthenticated)
        {
            return CommentOn(commentOn, isAuthenticated, String.Format("Comment {0}", UIHelperBase.GenerateUniqueIdentifier()));
        }

        public Comment CommentOn(IContent commentedOn, bool isAuthenticated, string commentText)
        {
            if (isAuthenticated)
            {
                return CommentOn(commentedOn, commentText, null, null, null);
            }
            else
            {
                string uniqueIdentifier = UIHelperBase.GenerateUniqueIdentifier();
                return CommentOn(commentedOn, commentText, String.Format("User {0}",uniqueIdentifier), String.Format("{0}@microsoft.com",uniqueIdentifier), null);
            }
        }

        public Comment CommentOn(IContent commentedOn, string commentText, string name, string email, string url)
        {           
            if (commentedOn == null)
                throw new CaseErrorException(new ArgumentNullException("commentedOn"));

            if (commentedOn is Post)
            {
                Post post = commentedOn as Post;
                selenium.Open(String.Format("/{0}/{1}", post.ContainerBlog.Permalink, post.Permalink));
            }
            if (commentedOn is Page)
            {
                Page page = commentedOn as Page;
                selenium.Open(String.Format("/{0}",page.Permalink));
            }

            if (selenium.IsElementPresent("comment-text"))
            {
                selenium.Type("comment-text", commentText);
                if (selenium.IsElementPresent("//input[@id='Name']") && selenium.IsElementPresent("//input[@id='Email']") && selenium.IsElementPresent("//input[@id='SiteName']"))
                {
                    selenium.Type("Name", name);
                    selenium.Type("Email", email);
                    selenium.Type("SiteName", url);
                }
                else
                {
                    string nameLocator = "//a[@href='/Users/Account/ChangePassword']";                   ;
                    if (selenium.IsElementPresent(nameLocator))
                    {
                        name = selenium.GetText(nameLocator);
                    }                    
                }
            }

            Submit();          
            return new Comment(new CommentAuthor(name, email, url), commentText, DateTime.Now, commentedOn);
        }

        public void ClickCommentedOnLink(Comment comment)
        {
            int index = GetIndexOfComment(comment);
            ClickAndWait(String.Format("{0}[{1}]/td[5]/a", CommentsLocator, index));
        }

        public void ClickCommentTimeLink(Comment comment)
        {
            int index = GetIndexOfComment(comment);
            ClickAndWait(String.Format("{0}[{1}]/td[4]/p[1]/a", CommentsLocator, index));
        }

        public bool IsCommentPresent(Comment comment)
        { 
            var index = GetIndexOfComment(comment);
            if (index >= 1)
            {
                return true;
            }
            return false;
        }    

        public bool IsCommentStatusExpected(Comment comment, CommentStatus statusExpected)
        {
            int index = GetIndexOfComment(comment);
            string statusExpectedStr = null;
            switch(statusExpected)
            {
                case CommentStatus.Approved:
                    statusExpectedStr = "Approved";
                    break;
                case CommentStatus.Pending:
                    statusExpectedStr = "Pending";
                    break;
                case CommentStatus.Spam:
                    statusExpectedStr= "Spam";
                    break;
            }
            string statusActual = GetStatusOfCommentIndex(index);
            return (statusActual == statusExpectedStr);
        }

        public bool IsFilterdCommentsAllStatusExpected(CommentStatus statusExpected)
        {
            int count = (int)selenium.GetXpathCount(CommentsLocator);
            string statusExpectedStr = null;
            switch (statusExpected)
            {
                case CommentStatus.Approved:
                    statusExpectedStr = "Approved";
                    break;
                case CommentStatus.Pending:
                    statusExpectedStr = "Pending";
                    break;
                case CommentStatus.Spam:
                    statusExpectedStr = "Spam";
                    break;
            }
            for (int index = 1; index <= count; index++)
            {
                string statusActual = GetStatusOfCommentIndex(index);
                if (statusActual == statusExpectedStr)
                {
                    continue;
                }
                else
                {
                    return false;
                }
            }
            return true;
        }

        public void DoActionToComment(Comment comment, CommentAction action)
        {   
            int index = GetIndexOfComment(comment);
            string actionText = null;
            switch (action)
            {
                case CommentAction.Approve:
                    actionText = "Approve";
                    break;
                case CommentAction.Unapprove:
                    actionText = "Unapprove";
                    break;
                case CommentAction.MarkAsSpam:
                    actionText = "Spam";
                    break;
                case CommentAction.Edit:
                    actionText = "Edit";
                    break;
                case CommentAction.Delete:
                    actionText = "Delete";
                    break;
            }
            int actionIndex = GetActionIndexByText(index, actionText);
            ClickAndWait(String.Format("{0}[{1}]/td[6]/div/a[{2}]", CommentsLocator, index, actionIndex));

        }

        public void DoSelectActionToComment(Comment comment, CommentAction action)
        {
            ManageComments();          
            if (IsCommentPresent(comment))
            {
                CheckComment(comment);
                switch (action)
                {
                    case CommentAction.Approve:
                        selenium.Select("publishActions", String.Format("label=Approve"));
                        break;
                    case CommentAction.MarkAsSpam:
                        selenium.Select("publishActions", String.Format("label=Mark as Spam"));
                        break;
                    case CommentAction.Unapprove:
                        selenium.Select("publishActions", String.Format("label=Unapprove"));
                        break;
                    case CommentAction.Delete:
                        selenium.Select("publishActions", String.Format("label=Delete"));
                        break;
                    default:
                        break;
                }

                ClickAndWait("//button[@name='submit.BulkEdit']");
            }
        }

        public void DoActionToCommentForSpecifiedComponent(IContent commentedOn, Comment comment, CommentAction action)
        {
            int contentId = 0;
            if (commentedOn is Post)
            {
                var post = commentedOn as Post;
                contentId = post.Id;
            }
            if (commentedOn is Page)
            {
                var page = commentedOn as Page;
                contentId = page.Id;
            }
            selenium.Open(String.Format("/Admin/Comments/Details/{0}", contentId));
            selenium.Check("//input[@name='Comments[0].IsChecked']");
            switch (action)
            {
                case CommentAction.Approve:
                    selenium.Select("publishActions", String.Format("label=Approve"));
                    break;
                case CommentAction.MarkAsSpam:
                    selenium.Select("publishActions", String.Format("label=Mark as Spam"));
                    break;
                case CommentAction.Unapprove:
                    selenium.Select("publishActions", String.Format("label=Unapprove"));
                    break;
                case CommentAction.Delete:
                    selenium.Select("publishActions", String.Format("label=Delete"));
                    break;
                default:
                    break;
            }
            ClickAndWait("//button[@name='submit.BulkEdit']");
        }

        public void FilterComment(CommentFilter filter)
        {
            switch (filter)
            {
                case CommentFilter.All:
                    selenium.Select("filterResults", String.Format("label=All Comments"));
                    break;
                case CommentFilter.Approved:
                    selenium.Select("filterResults", String.Format("label=Approved Comments"));
                    break;
                case CommentFilter.Pending:
                    selenium.Select("filterResults", String.Format("label=Pending Comments"));
                    break;
                case CommentFilter.Spam:
                    selenium.Select("filterResults", String.Format("label=Spam"));
                    break;
            }

            ClickAndWait("//button[@name='submit.Filter']");           
        }

        public void FilterCommentForSpecifiedComponent(IContent commentedOn, CommentFilter filter)
        {
            int contentId = 0;
            if (commentedOn is Post)
            {
                var post = commentedOn as Post;
                contentId = post.Id;
            }
            if (commentedOn is Page)
            {
                var page = commentedOn as Page;
                contentId = page.Id;
            }
            selenium.Open(String.Format("/Admin/Comments/Details/{0}", contentId));
            FilterComment(filter);           
        }

        public void EditComment(Comment comment, string name, string email, string url, string commentText, CommentAction action)
        {
            DoActionToComment(comment, CommentAction.Edit);
           
            selenium.Type("Name", name);            
            selenium.Type("Email", email);
            selenium.Type("SiteName", url);
            selenium.Type("CommentText", commentText);

            switch (action)
            {
                case CommentAction.Approve:
                    selenium.Click("Status_Approved");
                    break;
                case CommentAction.Unapprove: 
                    selenium.Click("Status_Pending");
                    break;
                case CommentAction.MarkAsSpam:
                    selenium.Click("Status_Spam");
                    break;
                default:
                    break;
            }

            Submit();

            comment.Author.Name = name;
            comment.Author.Email = email;
            comment.Author.Url = url;
            comment.CommentText = commentText;
        }

        public void DisableEnableComments(IContent commentedOn, DisableEnableAction action)
        {
            int contentId = 0;
            if (commentedOn is Post)
            {
                Post post = commentedOn as Post;
                contentId = post.Id;
            }
            if (commentedOn is Page)
            {
                Page page = commentedOn as Page;
                contentId = page.Id;
            }
            selenium.Open(String.Format("/Admin/Comments/Details/{0}", contentId));
            
            if (action==DisableEnableAction.DisableComments && selenium.IsElementPresent("//button[@title='Disable Comments']"))
            {
                ClickAndWait("//button[@title='Disable Comments']");
            }
            else if(action == DisableEnableAction.EnableComments && selenium.IsElementPresent("//button[@title='Enable Comments']"))
            {
                ClickAndWait("//button[@title='Enable Comments']"); ;
            }
        }
    }

    public class Comment
    {
        public CommentAuthor Author { get; private set; }

        public string CommentText { get; set; }

        public DateTime DateTime { get; private set; }

        public IContent CommentedOn { get; private set; }

        public Comment(CommentAuthor author, string commentText, DateTime datetime, IContent commentedOn)
        {
            Author = author;
            CommentText = commentText;
            DateTime = datetime;
            CommentedOn = commentedOn;
        }
    }

    public class CommentAuthor
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Url { get; set; }

        public CommentAuthor(string name, string email, string url)
        {
            Name = name;
            Email = email;
            Url = url;
        }
    }

    public enum CommentAction
    {
        Approve, 
        MarkAsSpam,
        Unapprove,
        Delete,
        Edit,
        NoAction
    }

    public enum CommentFilter
    {
        Pending,
        Approved,
        Spam,
        All
    }

    public enum CommentStatus
    {
        Pending,
        Approved,
        Spam
    }

    public enum DisableEnableAction
    {
        DisableComments,
        EnableComments
    }
}
